/****************************************************
	文件：DictionaryMethod.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2021/11/4 9:58:47
	功能：
*****************************************************/

using System.Collections.Generic;
using HTFramework.Utility;

namespace GCOptimizing
{
    public class DictionaryMethod
    {
        public enum KeyEnum
        {
            Key1,
            Key2,
            Key3,
        }

        public class KeyStructComparer : IEqualityComparer<KeyStruct>
        {
            public bool Equals(KeyStruct x, KeyStruct y)
            {
                return x.Id == y.Id;
            }

            public int GetHashCode(KeyStruct obj)
            {
                return obj.Id;
            }
        }
        
        public struct KeyStruct
        {
            public int Id;
            
            public KeyStruct(int id)
            {
                Id = id;
            }
        }

        public class KeyClass
        {
            public int Id;
            
            public KeyClass(int id)
            {
                Id = id;
            }
        }

        Dictionary<int, int> mIntMap;
        Dictionary<KeyEnum, int> mEnumMap;
        Dictionary<string, int> mStringMap;
        string mKeyString1 = "12345";
        string mKeyString2 = "23456";
        Dictionary<KeyStruct, int> mStructMap1;
        Dictionary<KeyStruct, int> mStructMap2;
        KeyStruct mKeyStruct1, mKeyStruct2;
        KeyStructComparer mKeyStructComparer; //用于优化 Struct 作为 Dictionary key值时产生的 gc
        Dictionary<KeyClass, int> mClassMap;
        KeyClass mKeyClass1, mKeyClass2;

        const int LOOP = 1000;
        
        public DictionaryMethod()
        {
            mIntMap = new Dictionary<int, int>();
            mEnumMap = new Dictionary<KeyEnum, int>();
            mStringMap = new Dictionary<string, int>();
            mStringMap.Add(mKeyString1, 1);
            mStructMap1 = new Dictionary<KeyStruct, int>();
            mKeyStructComparer = new KeyStructComparer();
            mStructMap2 = new Dictionary<KeyStruct, int>(mKeyStructComparer);
            mKeyStruct1 = new KeyStruct(1);
            mKeyStruct2 = new KeyStruct(2);
            mStructMap1.Add(mKeyStruct1, 1);
            mStructMap2.Add(mKeyStruct1, 1);
            mClassMap = new Dictionary<KeyClass, int>();
            mKeyClass1 = new KeyClass(1);
            mKeyClass2 = new KeyClass(2);
            mClassMap.Add(mKeyClass1, 1);
        }

        public void Test()
        {
            /*
             * 测试结论：优先选择 int，其次 enum
             * Dictionary key 类型选择：
             * 性能：(循环1000次)
             *     int     >     enum    >= struct(优化后) >=     class    >=    string    >> struct(优化前)
             * (0.04/0.02)   (0.06/0.03)     (0.05/0.03)      (0.06/0.03)     (0.06/0.04)     (0.37/0.14) 
             */
            
            //int 
            mIntMap.Add(1, 1);
            RuntimeUtil.ProfilerSample("tw dictionary TryGetValue (int)",() =>
            {
                for (int i = 0; i < LOOP; i++)
                    mIntMap.TryGetValue(1, out var num); //0.04ms
            });
            RuntimeUtil.ProfilerSample("tw dictionary ContainsKey (int)", () =>
            {
                for (int i = 0; i < LOOP; i++)
                    mIntMap.ContainsKey(2); //0.02ms
            });
            
            //enum 
            mEnumMap.Add(KeyEnum.Key1, 1);
            RuntimeUtil.ProfilerSample("tw dictionary TryGetValue (enum)",() =>
            {
                for (int i = 0; i < LOOP; i++)
                    mEnumMap.TryGetValue(KeyEnum.Key1, out var num); //0.06ms
            });
            RuntimeUtil.ProfilerSample("tw dictionary ContainsKey (enum)", () =>
            {
                for (int i = 0; i < LOOP; i++)
                    mEnumMap.ContainsKey(KeyEnum.Key2); //0.03ms
            });
            
            //string 
            RuntimeUtil.ProfilerSample("tw dictionary TryGetValue (string)",() =>
            {
                for (int i = 0; i < LOOP; i++)
                    mStringMap.TryGetValue(mKeyString1, out var num); //0.06ms
            });
            RuntimeUtil.ProfilerSample("tw dictionary ContainsKey (string)", () =>
            {
                for (int i = 0; i < LOOP; i++)
                    mStringMap.ContainsKey(mKeyString2); //0.04ms
            });
            
            //struct 优化前
            RuntimeUtil.ProfilerSample("tw dictionary TryGetValue (struct) before",() =>
            {
                //key 存在
                //gc:60b, this.comparer.GetHashCode(key) 和 this.comparer.Equals(this.entries[index].key, key) 产生
                for (int i = 0; i < LOOP; i++)
                    mStructMap1.TryGetValue(mKeyStruct1, out var num); //0.37ms
            });
            RuntimeUtil.ProfilerSample("tw dictionary ContainsKey (struct) before", () =>
            {
                //key 不存在
                //gc:20b, this.comparer.GetHashCode(key) 产生
                for (int i = 0; i < LOOP; i++)
                    mStructMap1.ContainsKey(mKeyStruct2); //0.14ms
            });
            
            //struct 优化后
            RuntimeUtil.ProfilerSample("tw dictionary TryGetValue (struct) after",() =>
            {
                //key 存在
                //gc:0b
                for (int i = 0; i < LOOP; i++)
                    mStructMap2.TryGetValue(mKeyStruct1, out var num); //0.05ms
            });
            RuntimeUtil.ProfilerSample("tw dictionary ContainsKey (struct) after", () =>
            {
                //key 不存在
                //gc:0b
                for (int i = 0; i < LOOP; i++)
                    mStructMap2.ContainsKey(mKeyStruct2); //0.03ms
            });
            
            //class 
            RuntimeUtil.ProfilerSample("tw dictionary TryGetValue (class)",() =>
            {
                for (int i = 0; i < LOOP; i++)
                    mClassMap.TryGetValue(mKeyClass1, out var num); //0.06ms
            });
            RuntimeUtil.ProfilerSample("tw dictionary ContainsKey (class)", () =>
            {
                for (int i = 0; i < LOOP; i++)
                    mClassMap.ContainsKey(mKeyClass2); //0.03ms
            });
        }
    }
}